/****************************************************************

kernel/cpuid.c
CPU identification code for AWOS

See LICENSE file included with this source for copyright
information.

****************************************************************/

#include <kernel.h>
#include <string.h>

char *KiCpuBrandStringIntel[] =
{ "none. (does not support brand string)",
"Intel(R) Celeron(R) processor (01).",
"Intel(R) Pentium(R) III processor (02).",
"Intel(R) Pentium(R) III Xeon(TM) processor (03).",
"Intel(R) Pentium(R) III processor (04).",
"reserved (05).",
"Mobile Intel(R) Pentium(R) III-M processor (06).",
"Mobile Intel(R) Celeron(R) processor (07).",
"Intel(R) Pentium(R) 4 processor (08).",
"Intel(R) Pentium(R) 4 processor (09).",
"Intel(R) Celeron(R) processor (0A).",
"Intel(R) Xeon(TM) processor (0B).",
"Intel(R) Xeon(TM) MP processor (0C).",
"reserved (0D).",
"Mobile Intel(R) Pentium(R) 4-M processor (0E).",
"Mobile Intel(R) Celeron(R) processor (0F).",
"reserved (10).",
"Mobile Genuine Intel(R) processor (11).",
"Intel(R) Celeron(R) M processor (12).",
"Mobile Intel(R) Celeron(R) processor (13).",
"Intel(R) Celeron(R) processor (14).",
"Mobile Genuine Intel(R) processor (15).",
"Intel(R) Pentium(R) M processor (16).",
"Mobile Intel(R) Celeron(R) processor (17)." };

void KiCpuIdIntel(struct KiCpuInfo *);
void KiCpuIdAmd(struct KiCpuInfo *);
int KiGetCpuClass();

void KiCpuId(struct KiCpuInfo *cpu)
{
	long eax, ebx, ecx, edx;
	
	dprintf("Running on ");
	
	switch(KiGetCpuClass())
	{
		case 1:
			strcpy("GenuineIntel", cpu->mfr);
			cpu->features.family = 1;
			dprintf(" an Intel 8086/8088 or compatible.  I'm impressed.");
			return;
		case 2:
			strcpy("GenuineIntel", cpu->mfr);
			cpu->features.family = 2;
			dprintf(" an Intel286 or compatible.");
			return;
		case 3:
			strcpy("GenuineIntel", cpu->mfr);
			cpu->features.family = 3;
			dprintf(" an Intel386 or compatible.");
			return;
		case 4:
			strcpy("GenuineIntel", cpu->mfr);
			cpu->features.family = 4;
			dprintf(" an Intel486 below stepping 3.");
			return;
		case 5:
			/* fall through -- CPUID instruction supported */
			eax = ebx = ecx = edx = 0;
	};
	
	asm("CPUID" :
		"=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) :
		"a"(0)
	);
	
	memcpy(cpu->mfr, &ebx, sizeof(ebx));
	memcpy(cpu->mfr + sizeof(ebx), &edx, sizeof(edx));
	memcpy(cpu->mfr + sizeof(ebx) + sizeof(edx), &ecx, sizeof(ecx));
	cpu->mfr[12] = 0;
	
	if(!strcmp(cpu->mfr, "GenuineIntel"))
	{
		dprintf("an Intel");
		KiCpuIdIntel(cpu);
	} else if(!strcmp(cpu->mfr, "AuthenticAMD")) {
		dprintf("an AMD");
		KiCpuIdAmd(cpu);
	} else if(!strcmp(cpu->mfr, "CyrixInstead")) {
		dprintf("a Cyrix.");
	} else if(!strcmp(cpu->mfr, "CentaurHauls")) {
		dprintf("a Centaur.");
	} else if(!strcmp(cpu->mfr, "SiS SiS SiS ")) {
		dprintf("an SiS.");
	} else if(!strcmp(cpu->mfr, "NexGenDriven")) {
		dprintf("a NexGen.");
	} else if((!strcmp(cpu->mfr, "GenuineTMx86")) || (!strcmp(cpu->mfr, "TransmetaCPU"))) {
		dprintf("a Transmeta.");
	} else if(!strcmp(cpu->mfr, "RiseRiseRise")) {
		dprintf("a Rise.");
	} else if(!strcmp(cpu->mfr, "UMC UMC UMC ")) {
		dprintf("a UMC.");
	} else if(!strcmp(cpu->mfr, "Geode by NSC")) {
		dprintf("a National Semiconductor Geode.");
	} else {
		dprintf("an unknown manufacturer: '%s'.", cpu->mfr);
	}
	
	return;
}

void KiCpuIdIntel(struct KiCpuInfo *cpu)
{
	long eax, ebx, ecx, edx;
	
	asm("CPUID" :
		"=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) :
		"a"(1)
	);
	
	//0:3 - Stepping
	//4:7 - Model
	//8:11 - Family
	//12:13 - Processor Type
	//16:19 - Extended Model
	//20:27 - Extended Family
	cpu->features.stepping	= (eax   )  & 15;
	cpu->features.model	= (eax>>4)  & 15;
	cpu->features.family	= (eax>>8)  & 15;
	cpu->features.type	= (eax>>12) &  3;
	cpu->features.ext_model	= (eax>>16) & 15;
	cpu->features.ext_family= (eax>>20) & 63;
	cpu->features.brand	= (ebx    ) & 0xFFFF;
	cpu->features.c_flags	= ecx;
	cpu->features.d_flags	= edx;

	switch(cpu->features.family)
	{
		case 4:
			//486
			switch(cpu->features.model)
			{
				case 0:
				case 1:
					dprintf("486 DX (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 2:
					dprintf("486 SX (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 3:
					dprintf("DX2/DX2 OD/487 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 4:
					dprintf("486 SL (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 5:
					dprintf("SX2 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 6:
					dprintf("DX2 (Write-Back enhanced) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 7:
					dprintf("DX4/DX4 OD (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				default:
					dprintf("486-class unknown (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
			};
			break;
		case 5:
			//586 / Pentium
			switch(cpu->features.model)
			{
				case 1:
					dprintf(" Pentium (60/66) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 2:
					dprintf(" Pentium (75/133) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 3:
					dprintf(" Pentium Overdrive for Intel486 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 4:
					dprintf(" Pentium%swith MMX (f%d, m%d, s%d).", ((cpu->features.type == 1) ? " Overdrive " : " "), cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				default:
					dprintf(" Pentium-class unknown (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
			};
			break;
		case 6:
			//686 / Pentium Pro / Pentium II / Celeron / Pentium III
			switch(cpu->features.model)
			{
				case 1:
					dprintf(" Pentium Pro (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 3:
					dprintf(" Pentium II%s(Klamath) (f%d, m%d, s%d).", ((cpu->features.type == 1) ? " Overdrive " : " "), cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 5:
					dprintf(" Pentium II (Deschutes) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 6:
					dprintf(" Celeron (Mendocino) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 7:
					dprintf(" Pentium III (Katmai) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 8:
					dprintf(" Pentium III (Coppermine) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 9:
					dprintf(" Pentium M (Banias) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 10:
					dprintf(" Pentium III Xeon (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 11:
					dprintf(" Pentium III (Tualatin) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 13:
					dprintf(" Pentium/Celeron M model D (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 14:
					dprintf(" Core Solo/Duo (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
				default:
					dprintf(" Pentium II-class unknown (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
			};
			break;
		case 15:
			//786 / NetBurst / Pentium 4 / Celeron
			switch(cpu->features.model)
			{
				case 0:
					dprintf(" Pentium 4/Xeon/Celeron (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 1:
					dprintf(" Pentium 4/Xeon/Celeron (Willamette) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 2:
					dprintf(" Pentium 4/Xeon/Celeron (Northwood) (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				default:
					dprintf(" NetBurst unknown (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
			};
			break;
		default:
			dprintf(" unknown (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
	}
	
	return;
}

void KiCpuIdAmd(struct KiCpuInfo *cpu)
{
	long eax, ebx, ecx, edx;
	
	asm("CPUID" :
		"=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) :
		"a"(1)
	);
	
	cpu->features.stepping	= (eax   ) & 15;
	cpu->features.model	= (eax>>4) & 15;
	cpu->features.family	= (eax>>8) & 15;
	
	switch(cpu->features.family)
	{
		case 4:
			dprintf(" Am486 or Am5x86 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
			break;
		case 5:
			//Pentium-compatibles
			switch(cpu->features.model)
			{
				case 0:
					dprintf(" K5 model 0 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 1:
					dprintf(" K5 model 1 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 2:
					dprintf(" K5 model 2 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 3:
					dprintf(" K5 model 3 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 6:
					dprintf(" K6 model 6 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 7:
					dprintf(" K6 model 7 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 8:
					dprintf(" K6-2 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 9:
					dprintf(" K6-III (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				default:
					dprintf(" K5/K6 unknown (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
			};
			break;
		case 6:
			//Athlon
			switch(cpu->features.model)
			{
				case 1:
					dprintf(" K7 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 2:
					dprintf(" Athlon model 2 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 3:
					dprintf(" Duron model 3 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 4:
					dprintf(" Athlon model 4 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 6:
					dprintf(" Athlon/Duron MP/XP (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				case 7:
					dprintf(" Duron model 7 (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
				default:
					dprintf(" Athlon/Duron unknown (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
					break;
			};
			break;
		default:
			dprintf(" unknown (f%d, m%d, s%d).", cpu->features.family, cpu->features.model, cpu->features.stepping);
			break;
	};
}
